Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ESLint Plugin: Introduce rule json-schema-no-plain-object-types #21687

Closed

Conversation

mcsf
Copy link
Contributor

@mcsf mcsf commented Apr 17, 2020

Description

Require all "object" types within a block's attributes to define their expected properties (json-schema-no-plain-object-types).

Block attributes must conform to types as specified in the WordPress REST API documentation, a behavior based on JSON Schema. This schema is used to validate attribute data not only in the REST API, but also in the block editor.

This rule requires that any declaration of an object type specify the exact properties that make up the object. This applies to the type of attributes themselves as well as any data within an attribute, such as when an attribute of type array expects values of type object.

Status

This is an early exploration and not necessarily something to carry through. However, I think the conversation around how strictly we encourage blocks to specify their data models is important. A recent conversation in #21359 (comment) with @aduth sparked this PR. The question of whether violations of this rule should be seen as errors is up for discussion too.

As I haven't written rules for eslint-plugin before, I am sure I am forgetting things in this PR. So far:

  • Update package changelog

How has this been tested?

Addition of rule-specific unit tests.

Screenshots

Types of changes

Checklist:

  • My code is tested.
  • My code follows the WordPress code style.
  • My code follows the accessibility standards.
  • My code has proper inline documentation.
  • I've included developer documentation if appropriate.
  • I've updated all React Native files affected by any refactorings/renamings in this PR.

@mcsf mcsf added [Type] Code Quality Issues or PRs that relate to code quality [Package] ESLint plugin /packages/eslint-plugin labels Apr 17, 2020
@aduth
Copy link
Member

aduth commented Apr 17, 2020

This rule requires that any declaration of an object type specify the exact properties that make up the object.

Do we actually validate this?

To clarify, I don't necessarily see it as a blocker, since this effort can be future-compatible to some future enhancement to implement the validation.

@github-actions
Copy link

Size Change: 0 B

Total Size: 841 kB

ℹ️ View Unchanged
Filename Size Change
build/a11y/index.js 1.02 kB 0 B
build/annotations/index.js 3.62 kB 0 B
build/api-fetch/index.js 4.01 kB 0 B
build/autop/index.js 2.82 kB 0 B
build/blob/index.js 620 B 0 B
build/block-directory/index.js 6.24 kB 0 B
build/block-directory/style-rtl.css 760 B 0 B
build/block-directory/style.css 761 B 0 B
build/block-editor/index.js 105 kB 0 B
build/block-editor/style-rtl.css 10.2 kB 0 B
build/block-editor/style.css 10.2 kB 0 B
build/block-library/editor-rtl.css 7.08 kB 0 B
build/block-library/editor.css 7.09 kB 0 B
build/block-library/index.js 112 kB 0 B
build/block-library/style-rtl.css 7.17 kB 0 B
build/block-library/style.css 7.17 kB 0 B
build/block-library/theme-rtl.css 683 B 0 B
build/block-library/theme.css 685 B 0 B
build/block-serialization-default-parser/index.js 1.88 kB 0 B
build/block-serialization-spec-parser/index.js 3.1 kB 0 B
build/blocks/index.js 57.7 kB 0 B
build/components/index.js 198 kB 0 B
build/components/style-rtl.css 16.9 kB 0 B
build/components/style.css 16.9 kB 0 B
build/compose/index.js 6.66 kB 0 B
build/core-data/index.js 11.2 kB 0 B
build/data-controls/index.js 1.25 kB 0 B
build/data/index.js 8.43 kB 0 B
build/date/index.js 5.47 kB 0 B
build/deprecated/index.js 772 B 0 B
build/dom-ready/index.js 569 B 0 B
build/dom/index.js 3.1 kB 0 B
build/edit-navigation/index.js 3.54 kB 0 B
build/edit-navigation/style-rtl.css 485 B 0 B
build/edit-navigation/style.css 485 B 0 B
build/edit-post/index.js 27.9 kB 0 B
build/edit-post/style-rtl.css 12.3 kB 0 B
build/edit-post/style.css 12.3 kB 0 B
build/edit-site/index.js 10.5 kB 0 B
build/edit-site/style-rtl.css 5.05 kB 0 B
build/edit-site/style.css 5.05 kB 0 B
build/edit-widgets/index.js 7.49 kB 0 B
build/edit-widgets/style-rtl.css 4.67 kB 0 B
build/edit-widgets/style.css 4.67 kB 0 B
build/editor/editor-styles-rtl.css 428 B 0 B
build/editor/editor-styles.css 431 B 0 B
build/editor/index.js 43.3 kB 0 B
build/editor/style-rtl.css 3.48 kB 0 B
build/editor/style.css 3.47 kB 0 B
build/element/index.js 4.65 kB 0 B
build/escape-html/index.js 733 B 0 B
build/format-library/index.js 7.32 kB 0 B
build/format-library/style-rtl.css 502 B 0 B
build/format-library/style.css 502 B 0 B
build/hooks/index.js 2.13 kB 0 B
build/html-entities/index.js 622 B 0 B
build/i18n/index.js 3.56 kB 0 B
build/is-shallow-equal/index.js 711 B 0 B
build/keyboard-shortcuts/index.js 2.51 kB 0 B
build/keycodes/index.js 1.91 kB 0 B
build/list-reusable-blocks/index.js 3.12 kB 0 B
build/list-reusable-blocks/style-rtl.css 226 B 0 B
build/list-reusable-blocks/style.css 226 B 0 B
build/media-utils/index.js 5.28 kB 0 B
build/notices/index.js 1.79 kB 0 B
build/nux/index.js 3.4 kB 0 B
build/nux/style-rtl.css 616 B 0 B
build/nux/style.css 613 B 0 B
build/plugins/index.js 2.67 kB 0 B
build/primitives/index.js 1.49 kB 0 B
build/priority-queue/index.js 789 B 0 B
build/redux-routine/index.js 2.84 kB 0 B
build/rich-text/index.js 14.8 kB 0 B
build/server-side-render/index.js 2.67 kB 0 B
build/shortcode/index.js 1.7 kB 0 B
build/token-list/index.js 1.28 kB 0 B
build/url/index.js 4.02 kB 0 B
build/viewport/index.js 1.84 kB 0 B
build/warning/index.js 1.14 kB 0 B
build/wordcount/index.js 1.17 kB 0 B

compressed-size-action

@aduth
Copy link
Member

aduth commented Apr 17, 2020

It's a common point of strain in the implementation of these custom rules, but: Are "blocks" a thing that @wordpress/eslint-plugin (an otherwise generic set of ESLint lint configurations and rules) should be aware of / concerned with?

The answer may very well be "Yes". Some of the existing rules could similarly qualify as being pretty WordPress-specific (i18n rules, for example).

@ghost
Copy link

ghost commented Apr 18, 2020

@mcsf I was thinking about this really but I don't know why attributes schema model wasn't structured like JSON schema model from first. It looks like custom schema built for Gutenberg rather than JSON schema.
I saw your example on the merge request:

( {
    "name": "my/gallery",
    "category": "widgets",
    "attributes": {
        "images": {
            "type": "array",
            "items": {
                "type": "object",
                "properties": {
                    "id": {
                        "type": "number"
                    },
                    "width": {
                        "type": "number"
                    }
                },
               "required": ['id', 'width']
            }
        }
    }
} )

But what if I would like to have the images attribute itself required
I was thinking it would be more appropriate to have the registration api like the following:

({
  name: "my/gallery",
  category: "widgets",
  attributesSchema: {
    properties: {
      images: {
        type: "array",
        items: {
          type: "object",
          properties: {
            id: {
              type: "number"
            },
            width: {
              type: "number"
            }
          },
          required: [ "id", "width" ]
        }
      }
    },
    required: [ "image" ],
    additionalProperties: false
  },
  // For having default attributes
  initialAttributes: {
  }
});

and in registration, we force {type: "object"} to be merged with attributesSchema because of course attributes must be an object
I am just sharing my thoughts to have like a standard api for this schema model like the Rest api standards.

@ghost
Copy link

ghost commented Apr 18, 2020

However, I believe my suggestion is so late because how the things got structured already, there won't be such a flexibility.
Like for example:

{
    url: {
        type: 'string',
        source: 'attribute',
        selector: 'img',
        attribute: 'src',
    }
}

Extracting the src attribute from an img found in the block’s markup.
This isn't like JSON schema by any way and to re-structure the attributes schema to be exactly like JSON schema, this might be impossible at this phase.
But please don't get me wrong, I agree with this merge request.

@mcsf
Copy link
Contributor Author

mcsf commented Apr 20, 2020

This rule requires that any declaration of an object type specify the exact properties that make up the object.

Do we actually validate this?

As discussed: on the client we don't, though on the server the WP API does validate. Whatever we build in the future to actually use validation, I personally think there is already a benefit in requiring the developer to be explicit in data needs.

@mcsf
Copy link
Contributor Author

mcsf commented Apr 20, 2020

It's a common point of strain in the implementation of these custom rules, but: Are "blocks" a thing that @wordpress/eslint-plugin (an otherwise generic set of ESLint lint configurations and rules) should be aware of / concerned with?

I battled with this myself, and I think you can tell in the fact that I zigzag between mentions of "JSON schema" (dubious) and "block attributes" (also dubious because the rule obviously doesn't actually analyze the declarations).

Keeping small in scope and generic enough to be compatible with the WP API can help justify that it remain a part of a WP set of rules, as you suggest:

The answer may very well be "Yes". Some of the existing rules could similarly qualify as being pretty WordPress-specific (i18n rules, for example).

@mcsf
Copy link
Contributor Author

mcsf commented Apr 20, 2020

Hi, @muhammedmagdi —

I was thinking about this really but I don't know why attributes schema model wasn't structured like JSON schema model from first. It looks like custom schema built for Gutenberg rather than JSON schema.

Indeed, though the attribute model in Gutenberg was influenced by JSON Schema, it was shaped by more pressing concerns around attribute sourcing, concerns that JSON Schema might not have responded to. Roughly, the only things that mattered for an attribute would be its base type and, if any, its default value; internally, the only validation really happening was the casting of values to the base type.

Meanwhile, being able to declare complex sourcing of attributes was a much more powerful feature for Gutenberg. It allowed attributes to be sourced from the actual block markup, thereby avoiding the need to duplicate data between the data encoded in the block boundary and the markup. It also allowed the use of completely different sources: an attribute may live in a post's metadata, for instance.

But what if I would like to have the images attribute itself required

Attributes themselves shouldn't need the required property because this doesn't address an actual issue in Gutenberg: attributes either have a default value, in which case the need for an value is always fulfilled, or they should handle empty states manually — which is already necessarily mediated by a block's edit method.

This doesn't preclude some sort of required support for attributes, but I don't know what that would look like, nor what real problems it would solve.

However, I believe my suggestion is so late because how the things got structured already, there won't be such a flexibility.

Correct, the current schema is here to stay, even if we allow optional enhancements in the future.

JSON Schema is a very expressive system which, if fully taken advantage of, would likely be "too much" for Gutenberg. What prompted this pull request was the observation that, over time, blocks that grow in complexity tend to escape Gutenberg's attribute validation system by introducing "wildcard" attributes that can contain anything. We can mitigate this by requiring arrays to specify items: { type: 'foo' } and objects to specify properties: { ... }.

@TimothyBJacobs
Copy link
Member

In the same vein, it would be great to add a similar check for ensuring that array types have a specified items.

@mcsf
Copy link
Contributor Author

mcsf commented Apr 27, 2020

In the same vein, it would be great to add a similar check for ensuring that array types have a specified items.

@TimothyBJacobs: if agree with the general direction, then yes, arrays should be addressed too. What do you think about #21687 (comment) and #21687 (comment)?

I personally think it's a worthwhile change in itself: it both guides developers and requires something of them, even if the editor doesn't validate the data.

@gziolo
Copy link
Member

gziolo commented Nov 28, 2020

What’s the status of this PR? 😃

@mcsf
Copy link
Contributor Author

mcsf commented Nov 30, 2020

What’s the status of this PR? 😃

It's been a while. Personally, do you think it has value? This was a nice thing to try out, but I don't feel strongly about getting it in. :)

@gziolo
Copy link
Member

gziolo commented Dec 8, 2020

I did another check for this PR and I found one fundamental issue with this proposal. While I'm personally very much in favor of having this type of validation added, it wouldn't be useful in the actual form simply because we have all block definitions in block.json file now. Do you think we could include it in the application logic instead? Maybe something enabled only in the development mode similar to how React warnings work?

@mcsf
Copy link
Contributor Author

mcsf commented Dec 8, 2020

While I'm personally very much in favor of having this type of validation added, it wouldn't be useful in the actual form simply because we have all block definitions in block.json file now.

It makes sense to target block.json files, but isn't that just a matter of configuring ESLint to lint those files too? I'm not sure I understood this:

Do you think we could include it in the application logic instead? Maybe something enabled only in the development mode similar to how React warnings work?

@gziolo
Copy link
Member

gziolo commented Dec 8, 2020

Do you think we could include it in the application logic instead? Maybe something enabled only in the development mode similar to how React warnings work?

Rather than using ESLint, we could run JSON schema validation on the full block definition inside registerBlockType at runtime but only in the development env. What I meant is that it would be completely removed in production mode.

@gziolo
Copy link
Member

gziolo commented Dec 8, 2020

It makes sense to target block.json files, but isn't that just a matter of configuring ESLint to lint those files too?

See: https://www.npmjs.com/package/eslint-plugin-json#is-eslint-really-the-best-tool-to-lint-my-json

Is eslint really the best tool to lint my JSON?

Not really. eslint plugin interface wasn't designed to lint a completely different language but its interface is flexible enough to allow it. So this plugin is certainly unusual.

Ideally, your editor would natively supports linting JSON. If it doesn't though, then might as well use this plugin. Hacky linting is better than no linting :)

@mcsf
Copy link
Contributor Author

mcsf commented Dec 9, 2020

Thanks, that makes sense.

Base automatically changed from master to trunk March 1, 2021 15:43
@mcsf
Copy link
Contributor Author

mcsf commented Jul 7, 2021

This feels very low-priority to me, and our PR count is high enough as it is, so I'm going to close it. Anyone should feel free to reopen if they want to pursue this. Thanks for the debate.

@mcsf mcsf closed this Jul 7, 2021
@mcsf mcsf deleted the add/eslint-rule-json-schema-no-plain-object-types branch July 7, 2021 14:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
[Package] ESLint plugin /packages/eslint-plugin [Type] Code Quality Issues or PRs that relate to code quality
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants